/**
 * License would add here
 */

#ifndef UTILS_JS_VALUE_H
#define UTILS_JS_VALUE_H

#include <node_api.h>

#include "js_array.h"
#include "js_bigint.h"
#include "js_constructor.h"
#include "js_date.h"
#include "js_function.h"
#include "js_native_api_types.h"
#include "js_number.h"
#include "js_object.h"
#include "js_string.h"
#include "js_symbol.h"
#include "js_value_base.h"
#include "js_value_ref.h"

class HandleScope : public NonAllocable {
public:
    HandleScope(napi_env env)
        : env_(env)
    {
        napi_open_handle_scope(env, &scope_);
    }
    ~HandleScope() { napi_close_handle_scope(env_, scope_); }

private:
    napi_env env_ { nullptr };
    napi_handle_scope scope_ { nullptr };
};

class EscapableHandleScope : NonAllocable {
public:
    EscapableHandleScope(napi_env env)
        : env_(env)
    {
        napi_open_escapable_handle_scope(env, &scope_);
    }
    ~EscapableHandleScope() { napi_close_escapable_handle_scope(env_, scope_); }

    inline napi_value Escape(napi_value value)
    {
        napi_value result = nullptr;
        napi_escape_handle(env_, scope_, value, &result);
        return result;
    }

    JSValue Escape(JSValue& value)
    {
        JSValue result(env_);
        napi_escape_handle(env_, scope_, value, &result);
        result.UpdateType();
        return result;
    }

private:
    napi_env env_ { nullptr };
    napi_escapable_handle_scope scope_ { nullptr };
};

class CallbackInfo : public NonAllocable {
public:
    CallbackInfo(napi_env env, napi_callback_info info)
        : env_(env)
    {
        napi_get_cb_info(env, info, &argc_, nullptr, &thisVar_, &data_);
        if (argc_ > 0) {
            argv_ = new napi_value[argc_];
            napi_get_cb_info(env, info, &argc_, argv_, nullptr, nullptr);
        }
    }
    ~CallbackInfo()
    {
        if (argc_ > 0) {
            delete[] argv_;
        }
    }

    inline size_t GetArgc() const { return argc_; }
    inline napi_value* GetArgs() const { return argv_; }

    inline JSValue GetArg(size_t index) const
    {
        if (index >= argc_) {
            return JSValue::GetUndefined(env_);
        }
        return JSValue(env_, argv_[index]);
    }
    inline JSValue operator[](size_t index) const
    {
        return GetArg(index);
    }

private:
    napi_env env_ { nullptr };
    size_t argc_ { 0 };
    napi_value* argv_ { nullptr };
    napi_value thisVar_ { nullptr };
    void* data_ { nullptr };
};

#endif /* UTILS_JS_VALUE_H */
